package models

import (
	"strings"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/httpmvc/model"
	"gorm.io/datatypes"

	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models/query"
)

type SysDevVariable struct {
	Sn         string          `json:"sn" gorm:"primaryKey;autoIncrement:false;type:varchar(64);comment:设备序列号"`
	Key        string          `json:"key" gorm:"primaryKey;autoIncrement:false;type:varchar(255);comment:变量标识"`
	Label      string          `json:"label" gorm:"type:varchar(255);comment:变量名称"`
	Type       string          `json:"type" gorm:"type:varchar(32);comment:变量类型"`
	Value      string          `json:"value" gorm:"type:text;comment:变量值"`
	Config     datatypes.JSON  `json:"config" gorm:"type:jsonb;comment:变量配置"`
	Param      datatypes.JSON  `json:"param" gorm:"type:jsonb;comment:变量扩展"`
	ReportTime *cDto.LocalTime `json:"reportTime" gorm:"type:timestamptz(6);comment:变量上报时间"`
	UpdateTime *cDto.LocalTime `json:"updateTime" gorm:"type:timestamptz(6);comment:变量平台时间"`
	Index      *int            `json:"index" gorm:"type:int2;comment:变量序号"`

	model.Model

	DevName      string `json:"devName" gorm:"-"`
	DevType      string `json:"devType" gorm:"-"`
	DeptId       string `json:"deptId" gorm:"-"`
	DeptName     string `json:"deptName" gorm:"-"`
	OnlineStatus string `json:"onlineStatus" gorm:"-"`
	AlarmStatus  bool   `json:"alarmStatus" gorm:"-"`
}

func (SysDevVariable) TableName() string {
	return "sys_dev_variable"
}

func (e *SysDevVariable) Generate() interface{} {
	o := *e
	return &o
}

func (e *SysDevVariable) FindVariableList(req *query.SysDevVariableQuery) (err error, result interface{}) {
	devQuery := query.SysDevQuery{UserInfo: req.UserInfo, DeptIdNot: req.DeptIdNot, DeptId: req.DeptId, OnlineStatus: req.OnlineStatus, NameSearch: req.DevName, SnSearch: req.DevSn, ProductModelNotInStr: req.ProductNot}
	// 查一堆东西
	devModel := &SysDev{}
	devModel.Orm = e.Orm
	snList := make([]string, 0)
	devList := make([]SysDev, 0)
	snDev := make(map[string]SysDev)
	if err = devModel.FindList(&devList, cDto.MakeCondition(devQuery.GetNeedSearch())); err != nil {
		return
	}
	// tag查询 预处理
	var tagSns, tagKeys []string
	if len(req.TagEq) > 0 {
		tagSns = make([]string, 0)
		tagKeys = make([]string, 0)
		rs := make([]map[string]interface{}, 0)
		if err = e.Orm.Raw("select * from sys_dev_variable,jsonb_array_elements((param->>'tags')::jsonb) as h where h :: VARCHAR = '\"" + req.TagEq + "\"'").Scan(&rs).Error; err != nil {
			return
		}
		if len(rs) == 0 {
			result = norecord(req.PageIndex, req.PageSize)
			return
		}
		for _, m := range rs {
			tagSns = append(tagSns, m["sn"].(string))
			tagKeys = append(tagKeys, m["key"].(string))
		}
	}
	// 权限处理
	req.GetNeedSearch()
	querySnIn := req.SnIn
	queryKeyIn := req.KeyIn
	for i, dev := range devList {
		if tagSns != nil && !utils.StrIn(dev.Sn, tagSns) {
			continue
		}
		snList = append(snList, dev.Sn)
		snDev[dev.Sn] = devList[i]
	}
	req.SnIn = snList
	if len(querySnIn) > 0 {
		// 取交集
		for i := len(req.SnIn) - 1; i >= 0; i-- {
			if !utils.StrIn(req.SnIn[i], querySnIn) {
				req.SnIn = append(req.SnIn[:i], req.SnIn[i+1:]...)
			}
		}
		if len(req.SnIn) == 0 {
			result = norecord(req.PageIndex, req.PageSize)
			return
		}
	}
	// 变量告警状态
	alarmSnKeys := make([]string, 0)
	alarmRow, _ := env.Cache.HashGetAll(constant.HasAlarmRowCache)
	for ar := range alarmRow {
		alarmSnKeys = append(alarmSnKeys, ar[:strings.LastIndex(ar, "|")])
	}
	if req.AlarmStatus != nil {
		// sn|key拼接的in/nin
		if *req.AlarmStatus {
			req.SnKeyConcatIn = alarmSnKeys
		} else {
			req.SnKeyConcatNIn = alarmSnKeys
		}
	}
	if len(tagKeys) > 0 {
		if len(req.KeyIn) > 0 {
			// 取交集
			for i := len(req.KeyIn) - 1; i >= 0; i-- {
				if !utils.StrIn(req.KeyIn[i], tagKeys) {
					req.KeyIn = append(req.KeyIn[:i], req.KeyIn[i+1:]...)
				}
			}
			if len(req.KeyIn) == 0 {
				result = norecord(req.PageIndex, req.PageSize)
				return
			}
		} else {
			req.KeyIn = tagKeys
		}
	}
	if len(queryKeyIn) > 0 {
		// 取交集
		for i := len(req.KeyIn) - 1; i >= 0; i-- {
			if !utils.StrIn(req.KeyIn[i], queryKeyIn) {
				req.KeyIn = append(req.KeyIn[:i], req.KeyIn[i+1:]...)
			}
		}
		if len(req.KeyIn) == 0 {
			result = norecord(req.PageIndex, req.PageSize)
			return
		}
	}
	// 查询变量
	list := make([]SysDevVariable, 0)
	var count int64
	if req.PageIndex > 0 && req.PageSize > 0 {
		err = e.FindPage(&list, &count,
			cDto.MakeCondition(*req),
			cDto.OrderDest("sn,key", true, req.OrderBy),
			cDto.Paginate(req.GetPageSize(), req.GetPageIndex()),
		)
		groupIdName, _ := utils.GroupIdName(e.Orm)
		for i := range list {
			sn := list[i].Sn
			if dev, ok := snDev[sn]; ok {
				list[i].DeptId = dev.DeptId
				list[i].DeptName = groupIdName[dev.DeptId]
				list[i].DevName = dev.Name
				list[i].DevType = dev.Type
				list[i].OnlineStatus = dev.OnlineStatus
			}
			fd := sn + "|" + list[i].Key
			if utils.StrIn(fd, alarmSnKeys) {
				list[i].AlarmStatus = true
			}
		}
		result = &cDto.Page{
			Records:   list,
			Total:     count,
			PageSize:  req.GetPageSize(),
			PageIndex: req.GetPageIndex(),
			Pages:     (count + int64(req.GetPageSize()) - 1) / int64(req.GetPageSize()),
			Other:     nil,
		}
	} else {
		err = e.FindList(&list,
			cDto.MakeCondition(*req),
			cDto.OrderDest("sn,key", true, req.OrderBy),
		)
		groupIdName, _ := utils.GroupIdName(e.Orm)
		for i := range list {
			sn := list[i].Sn
			if dev, ok := snDev[sn]; ok {
				list[i].DeptId = dev.DeptId
				list[i].DeptName = groupIdName[dev.DeptId]
				list[i].DevName = dev.Name
				list[i].DevType = dev.Type
				list[i].OnlineStatus = dev.OnlineStatus
			}
			fd := sn + "|" + list[i].Key
			if utils.StrIn(fd, alarmSnKeys) {
				list[i].AlarmStatus = true
			}
		}
		result = list
	}
	return
}

func (e *SysDevVariable) GenFromSchemaProfile(sn string, devSchema, devConfig map[string]interface{}, varKeyMap map[string]map[string]interface{}) (vars []SysDevVariable) {
	vars = make([]SysDevVariable, 0)
	sensorSchema := utils.GetFieldFromJson([]string{"properties", "sensor", "properties"}, devSchema).(map[string]interface{})
	flattenSensorSchema := map[string]interface{}{}
	utils.FlattenJson(sensorSchema, flattenSensorSchema, []string{})
	profileConfigSensor := utils.GetFieldFromJson([]string{"config", "sensor"}, devConfig)
	// 全量key
	allKeys := make([]string, 0)
	// key-title
	keyTitle := make(map[string]interface{})
	// key-type
	keyType := make(map[string]interface{})
	for k, v := range flattenSensorSchema {
		k = strings.ReplaceAll(k, ".properties.", ".")
		if strings.HasSuffix(k, ".title") {
			k = strings.ReplaceAll(k, ".title", "")
			keyTitle[k] = v
		} else if strings.HasSuffix(k, ".type") {
			k = strings.ReplaceAll(k, ".type", "")
			keyType[k] = v
		}
		if !utils.StrIn(k, allKeys) {
			allKeys = append(allKeys, k)
		}
	}
	// 形成结果
	for _, key := range allKeys {
		tmpVar := SysDevVariable{
			Sn:    sn,
			Key:   key,
			Label: key,
		}
		if title, ok := keyTitle[key]; ok && title != nil {
			tmpVar.Label = title.(string)
		}
		if tp, ok := keyType[key]; ok && tp != nil {
			tmpVar.Type = tp.(string)
		}
		if profileConfigSensor != nil {
			config := utils.GetFieldFromJson(strings.Split(key, "."), profileConfigSensor.(map[string]interface{}))
			if config != nil {
				tmpVar.Config = utils.MapToBytes(config.(map[string]interface{}))
			}
		}
		if m, ok := varKeyMap[key]; ok && m != nil {
			if index, ok := m["index"]; ok {
				idx := index.(int)
				tmpVar.Index = &idx
			}
			if m["value"] != nil {
				tmpVar.Value = m["value"].(string)
			}
			if m["updateTime"] != nil {
				upt := m["updateTime"].(cDto.LocalTime)
				tmpVar.UpdateTime = &upt
			}
			if m["param"] != nil {
				tmpVar.Param = []byte(m["param"].(string))
			}
		}

		//if tmpVar.Type != "object" {
		vars = append(vars, tmpVar)
		//}
	}
	return
}

func norecord(pageIndex, pageSize int) interface{} {
	list := make([]map[string]interface{}, 0)
	if pageIndex > 0 && pageSize > 0 {
		return &cDto.Page{
			Records:   list,
			Total:     0,
			PageSize:  pageSize,
			PageIndex: pageIndex,
			Pages:     (0 + int64(pageSize) - 1) / int64(pageSize),
			Other:     nil,
		}
	} else {
		return list
	}
}
